
`include "common_header.verilog"

//  *************************************************************************
//  File : lpi_txhold_ctrl
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2008 MorethanIP
//  Muenchner Strasse 199, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Daniel Koehler
//  info@morethanip.com
//  *************************************************************************
//  Description : Control MAC transmit to stop gracefully when transmissions
//                have completed (tx FIFO empty) and start transmitting LPI.
//                Also controls the MAC to hold its transmission as long as
//                the lpi_txhold is asserted at the end of a low power cycle.
//                
//  Version     : $Id: lpi_txhold_ctrl.v,v 1.1 2011/06/14 12:31:58 dk Exp $
//  *************************************************************************

module lpi_txhold_ctrl (

   reset_txclk,
   tx_clk,
   tx_clk_ena,
   lpi_req,
   lpi_txhold,
   tx_empty,
   tx_dval,
   mac_lowp_ena,
   rs_lowp_ena);

input   reset_txclk;    //  Active High reset for xgmii_txclk domain
input   tx_clk;         //  XGMII transmit clock      
input   tx_clk_ena;     //  XGMII Transmit Clock Enable.
input   lpi_req;        //  LPI state is requested
input   lpi_txhold;     //  hold MAC from transmitting
input   tx_empty;       //  TX FIFO empty
input   tx_dval;        //  TX is transmitting
output  mac_lowp_ena;   //  to MAC TX statemachine, suppress fetching of frames
output  rs_lowp_ena;    //  to RS layer transmit LPI sequences

reg     mac_lowp_ena; 
reg     rs_lowp_ena; 

parameter STM_TYP_MNORMAL       = 2'd 0;
parameter STM_TYP_TX_WAIT       = 2'd 1;
parameter STM_TYP_TX_STOP       = 2'd 2;
parameter STM_TYP_RS_LPI_ACTIVE = 2'd 3;

reg     [1:0] state; 
reg     [1:0] nextstate; 
wire    tx_active; 
reg     [2:0] q_cnt; //  quiet counter to check when TX is stable in idle
wire    q_cnt_done; 

//  quiet counter
//  -------------

always @(posedge reset_txclk or posedge tx_clk)
   begin : qcntp
   if (reset_txclk == 1'b 1)
      begin
      q_cnt <= 3'b 000;	
      end
   else
      begin
      if (state == STM_TYP_MNORMAL | state != nextstate)
         begin
         q_cnt <= 3'b 000;	
         end
      else if (q_cnt != 3'b 111 & tx_clk_ena == 1'b 1 )
         begin
         q_cnt <= q_cnt + 3'b 001;	
         end
      end
   end


//  indicate end of wait time when counter reached max.
assign q_cnt_done = q_cnt[2] & q_cnt[1] & q_cnt[0]; 


//  indicate if TX is active having any data.
//  -------------
assign tx_active = ~tx_empty | tx_dval; 


//  ---
//  STM
//  ---
always @(posedge reset_txclk or posedge tx_clk)
   begin : stmr
   if (reset_txclk == 1'b 1)
      begin
      state <= STM_TYP_MNORMAL;	
      end
   else
      begin
      state <= nextstate;	
      end
   end

always @(state or tx_active or q_cnt_done or lpi_req or lpi_txhold or tx_dval)
   begin : stmc
   case (state)

//  ----------------
//  Normal Operation
//  ----------------
   STM_TYP_MNORMAL:
      begin
      if ((lpi_req == 1'b 1 | lpi_txhold == 1'b 1) & tx_active == 1'b 0)
         begin
         nextstate = STM_TYP_TX_WAIT;	
         end
      else
         begin
         nextstate = STM_TYP_MNORMAL;	
         end
      end

//  ----------------
//  Wait for TX to become silent and TX FIFO empty.
//  ----------------
   STM_TYP_TX_WAIT:
      begin
      if (tx_active == 1'b 1 | (lpi_req == 1'b 0 & lpi_txhold == 1'b 0))
         begin
         nextstate = STM_TYP_MNORMAL;	//  rewind quiet counter
         end
      else if (q_cnt_done == 1'b 1 )
         begin
         nextstate = STM_TYP_TX_STOP;	
         end
      else
         begin
         nextstate = STM_TYP_TX_WAIT;	
         end
      end

//  ----------------
//  Disable TX (hold any further transmissions).
//  ----------------
   STM_TYP_TX_STOP:
      begin
      if ((lpi_req == 1'b 0 & lpi_txhold == 1'b 0) | tx_dval == 1'b 1)
         begin
        //  end of hold, or race condition that TX started after having been disabled.
         nextstate = STM_TYP_MNORMAL;	
         end
      else if (lpi_req == 1'b 1 & q_cnt_done == 1'b 1 )
         begin
         nextstate = STM_TYP_RS_LPI_ACTIVE;	
         end
      else
         begin
         nextstate = STM_TYP_TX_STOP;	
         end
      end

//  ----------------
//  LPI state, instruct RS to transmit LPI sequences
//  ----------------
   STM_TYP_RS_LPI_ACTIVE:
      begin
      if (lpi_req == 1'b 0)
         begin
        //  end of cycle, LPI deasserts, but hold can still be active to block the TX
         nextstate = STM_TYP_TX_STOP;	
         end
      else
         begin
         nextstate = STM_TYP_RS_LPI_ACTIVE;	
         end

      end

   default:
      begin
      nextstate = STM_TYP_MNORMAL;	//  never reached
      end

   endcase
   end


//  --------
//  controls
//  --------
always @(posedge reset_txclk or posedge tx_clk)
   begin : ctlp
   if (reset_txclk == 1'b 1)
      begin
      mac_lowp_ena <= 1'b 0;	
      rs_lowp_ena  <= 1'b 0;	
      end
   else
      begin

      if (nextstate == STM_TYP_TX_STOP | nextstate == STM_TYP_RS_LPI_ACTIVE)
         begin
         mac_lowp_ena <= 1'b 1;	//  stop MAC TX statemachine
         end
      else
         begin
         mac_lowp_ena <= 1'b 0;	
         end

      if (state == STM_TYP_RS_LPI_ACTIVE)
         begin
         rs_lowp_ena <= 1'b 1;	//  transmit LPI to line
         end
      else
         begin
         rs_lowp_ena <= 1'b 0;	
         end
      end
   end


endmodule // module lpi_txhold_ctrl

